package com.android.JiYclocks;

import java.text.SimpleDateFormat;
import java.util.Date;

import com.android.JiYclock.AlarmAlertWakeLock;
import com.android.JiYclock.Log;
import com.android.JiYclock.R;

import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Parcel;


/**
 * Glue class: connects AlarmAlert IntentReceiver to AlarmAlertactivity.  Passes through Alarm ID.
 * 接收SetAlarm发出的广播
 * 然后根据此时手机的状态来进行处理，来决定是采用alarmAlert或AlarmAlertFullScreen来进行显示
 */
public class SpecialAlarmReceiver extends BroadcastReceiver {

    /** If the alarm is older than STALE_WINDOW seconds, ignore.  It
        is probably the result of a time or timezone change */
    private final static int STALE_WINDOW = 60 * 30;

    @Override
    public void onReceive(Context context, Intent intent) {
    	
    	
        if (SpecialAlarms.SPECIALALARM_KILLED.equals(intent.getAction())) {
            //如果说闹钟已经被杀掉，就赶紧更新notification
            updateNotification(context, (SpecialAlarm)
                    intent.getParcelableExtra(SpecialAlarms.SPECIALALARM_INTENT_EXTRA),
                    intent.getIntExtra(SpecialAlarms.SPECIALALARM_KILLED_TIMEOUT, -1));
            return;
        } else if (SpecialAlarms.SPECIALCANCEL_SNOOZE.equals(intent.getAction())) {
            SpecialAlarms.saveSnoozeAlert(context, -1, -1);
            return;
        }

        SpecialAlarm specialalarm = null;
        // Grab the alarm from the intent. Since the remote AlarmManagerService
        // fills in the Intent to add some extra data, it must unparcel the
        // Alarm object. It throws a ClassNotFoundException when unparcelling.
        // To avoid this, do the marshalling ourselves.
        /**
         * 从Intent中抓去到alarm,一旦AlarmManagerService在Intent中填满了Extra Data,那就必将Unparcel Alarm 
         * Object，而且如果Unparcelling，它将抛出一个ClassNotFoundException异常.
         * 为了避免这样，自己做Marshilling 
         */
        final byte[] data = intent.getByteArrayExtra(SpecialAlarms.SPECIALALARM_RAW_DATA);
        if (data != null) {
        	//读取在设置闹钟的是存入Parcel的Alarm对象
            Parcel in = Parcel.obtain();
            in.unmarshall(data, 0, data.length);
            in.setDataPosition(0);
            specialalarm = SpecialAlarm.CREATOR.createFromParcel(in);
        }

        if (specialalarm == null) {
            Log.v("AlarmReceiver failed to parse the alarm from the intent");
            return;
        }

        // Intentionally verbose: always log the alarm time to provide useful
        // information in bug reports.
        long now = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss.SSS aaa");
        Log.v("AlarmReceiver.onReceive() id " + specialalarm.id + " setFor "
                + format.format(new Date(specialalarm.time)));
                
        
        //如果当前时间大于闹钟的时间+STALE_WINDOW
       // 这个闹钟忽略
        if (now > specialalarm.time + STALE_WINDOW * 1000) {
          {
                Log.v("AlarmReceiver ignoring stale alarm");
            }
            return;
        }

        // Maintain a cpu wake lock until the AlarmAlert and AlarmKlaxon can
        // pick it up.
       //获得一个CPU WakeLock
        AlarmAlertWakeLock.acquireCpuWakeLock(context);

        /* Close dialogs and window shade */
        //关掉此刻系统的所有Dialog
        Intent closeDialogs = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        context.sendBroadcast(closeDialogs);

        // Decide which activity to start based on the state of the keyguard.
        Class c = SpecialAlarmAlert.class;
        KeyguardManager km = (KeyguardManager) context.getSystemService(
                Context.KEYGUARD_SERVICE);
        
//        inKeyguardRestrictedInputMode();       SDK 文档中关于inKeyguardRestrictedInputMode的注释
//        If keyguard screen is showing or in restricted key input mode 
//        (i.e. in keyguard password emergency screen). When in such
//        mode, certain keys, such as the Home key and the right soft keys, don't work.


        
        if (km.inKeyguardRestrictedInputMode()) {
        	//如果处于受限密码状态
            // Use the full screen activity for security.
            c = SpecialAlarmAlertFullScreen.class;
        }
    
        Intent alarmAlert = new Intent(context, c);
        alarmAlert.putExtra(SpecialAlarms.SPECIALALARM_INTENT_EXTRA, specialalarm);
        
        //Intent.Flag_ACTIVITY_NEW_TASK 将使Activity成为一个新Task的开始
        //Intent.FLAG_ACTIVITY_NO_USER_ACTION 这个标志将在Activity暂停之前阻止从最前方的Activity回调的onUserLeaveHint()。

        alarmAlert.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
        context.startActivity(alarmAlert);

        SpecialAlarms.disableSnoozeAlert(context, specialalarm.id);
        
//        if (!specialalarm.daysOfWeek.isRepeatSet()) {
//            SpecialAlarms.enableAlarm(context, specialalarm.id, false);
//        } else
        {
           
            SpecialAlarms.setNextAlert(context);
        }

        // Play the alarm alert and vibrate the device.
        //通过service启动闹钟和震动
        Intent playAlarm = new Intent(SpecialAlarms.SPECIALALARM_ALERT_ACTION);
        playAlarm.putExtra(SpecialAlarms.SPECIALALARM_INTENT_EXTRA, specialalarm);
        context.startService(playAlarm);

        // Trigger a notification that, when clicked, will show the alarm alert
        // dialog. No need to check for fullscreen since this will always be
        // launched from a user action.
        Intent notify = new Intent(context, SpecialAlarmAlert.class);
        notify.putExtra(SpecialAlarms.SPECIALALARM_INTENT_EXTRA, specialalarm);
        PendingIntent pendingNotify = PendingIntent.getActivity(context,
        		specialalarm.id, notify, 0);

        // Use the alarm's label or the default label as the ticker text and
        // main text of the notification.
        String label = specialalarm.getLabelOrDefault(context);
        Notification n = new Notification(R.drawable.stat_notify_alarm,
                label, specialalarm.time);
        n.setLatestEventInfo(context, label,
                context.getString(R.string.alarm_notify_text),
                pendingNotify);
        n.flags |= Notification.FLAG_SHOW_LIGHTS
                | Notification.FLAG_ONGOING_EVENT;
        n.defaults |= Notification.DEFAULT_LIGHTS;

        // Send the notification using the alarm id to easily identify the
        // correct notification.
        NotificationManager nm = getNotificationManager(context);
        nm.notify(specialalarm.id, n);
    }

    private NotificationManager getNotificationManager(Context context) {
        return (NotificationManager)
                context.getSystemService(Context.NOTIFICATION_SERVICE);
    }

    private void updateNotification(Context context, SpecialAlarm alarm, int timeout) {
    	
        NotificationManager nm = getNotificationManager(context);

        // If the alarm is null, just cancel the notification.
        if (alarm == null) {
            {
                Log.v("Cannot update notification for killer callback");
            }
            return;
        }

        // Launch SetAlarm when clicked.
        Intent viewAlarm = new Intent(context, SpecialSetAlarm.class);
        viewAlarm.putExtra(SpecialAlarms.ALARM_ID, alarm.id);
        PendingIntent intent = PendingIntent.getActivity(context, alarm.id, viewAlarm, 0);

        // Update the notification to indicate that the alert has been
        // silenced.
        String label = alarm.getLabelOrDefault(context);
        
        //通过图表，Lable，时间来构造一个Notification消息类
        Notification n = new Notification(R.drawable.stat_notify_alarm, label, alarm.time);
      //设置事件信息就是拉下标题后显示的内容
        n.setLatestEventInfo(context, label,context.getString(R.string.alarm_alert_alert_silenced, timeout), intent);
        n.flags |= Notification.FLAG_AUTO_CANCEL;
        // We have to cancel the original notification since it is in the
        // ongoing section and we want the "killed" notification to be a plain
        // notification.
        nm.cancel(alarm.id);
        nm.notify(alarm.id, n);
    }
}
